function sineFunctionGenerator() {}
sineFunctionGenerator.prototype = {
		"tick" : 0,
		"gain" : 100000,
		"increment" : 10,
		
		//--------------------------------------------------------------------------------------------
		// public methods
		//--------------------------------------------------------------------------------------------
		"getValue" : function() {
				var value = this.gain * Math.sin(this.tick * (Math.PI/180));
				this.tick += this.increment;
				return value;
		}
}
var sineGenerator = new sineFunctionGenerator();
/*
 * whiteNoise
 */
function whiteNoiseGenerator() { }
whiteNoiseGenerator.prototype = {
		"min" : -1000000.0,
		"max" : 1000000.0,
		
		//--------------------------------------------------------------------------------------------
		// public methods
		//--------------------------------------------------------------------------------------------
		"getNoise" : function(input) {
				return Math.floor(Math.random() * (this.max - this.min + 1)) + this.min;
		}
}
var whiteNoise = new whiteNoiseGenerator();

/*
 * perlinNoise1D
 */
function perlinNoise1DGenerator() {}
perlinNoise1DGenerator.prototype = {
		"persistence" : .2,
		"primes" : 0,
		"octaves" : 3,
		"primeset" : [ // element number must match octaves
				{
						p1 : 15731,
						p2 : 789221,
						p3 : 1376312589,
						p4 : 1073741824.0
				},
				{
						p1 : 8219,
						p2 : 692051,
						p3 : 2009807,
						p4 : 19468639.0
				},
				{
						p1 : 173177,
						p2 : 39770261,
						p3 : 1000984351,
						p4 : 12917269.0
				}
		],
		"noise" : function(x) {
				x = Math.pow(x<<13, x);
				return ( 1.0 - ( (x * (x * x * this.primeset[this.primes].p1 + this.primeset[this.primes].p2) + this.primeset[this.primes].p3) & 0x7fffffff) / this.primeset[this.primes].p4);
		},
		"cosineInterpolate" : function(a, b, x) {
				var ft = x * Math.PI;
				var f = (1 - Math.cos(ft)) * .5;
				return  a*(1-f) + b*f;
		},
		"smoothedNoise" : function(x) {
				return this.noise(x)/2  +  this.noise(x-1)/4  +  this.noise(x+1)/4;
		},
		"interpolatedNoise" : function(x) {
				var integer_X    = Math.floor(x);
				var fractional_X = x - integer_X;
				var v1 = this.smoothedNoise(integer_X);
				var v2 = this.smoothedNoise(integer_X + 1);
				return this.cosineInterpolate(v1 , v2 , fractional_X);
		},
		
		//--------------------------------------------------------------------------------------------
		// public methods
		//--------------------------------------------------------------------------------------------
		"getNoise" : function(input) {
				var total = 0;
				var p = this.persistence;
				var n = this.octaves;
				var i, frequency, amplitude;
				var oSimplexNoise = new SimplexNoise(5);
				for (i = 0; i < n; i++) {
						frequency = Math.pow(2, i);
						amplitude = Math.pow(p, i);
						this.primes = i;
						total = total + noiseR(input * frequency,input * frequency) * amplitude;
				}
				echo (1, "Perlin:  Input="+input+", Output="+total);
				echo (2, "Perlin:  freq="+frequency+", amp="+amplitude+", primes="+this.primes+", total="+total);
				return total;
		}
}
var perlinNoise1D = new perlinNoise1DGenerator();

/*
 * generates a constant for testing only
 */
function constantNoiseGenerator() {}
constantNoiseGenerator.prototype = {

		//--------------------------------------------------------------------------------------------
		// public methods
		//--------------------------------------------------------------------------------------------
		"getNoise" : function(input) {
				return 111234.5;
		}
}
var constantNoise= new constantNoiseGenerator();
/*
 * audioNoiseInterface
 * The interface from the audioLib callbacks to the audioNoise system
 */
function audioNoiseInterface(tempo, notesPerBeat) {
		this.tempo = tempo;
		this.notesPerBeat = notesPerBeat;
}
audioNoiseInterface.prototype = {

		"tempo" : 120,
		"notesPerBeat" : 4,

		"noteRange" : 100,                                      // C0-D#8
		"frequencyLowCutOff" : 133.0,                   // cant hear anything below this
		"frequencyHighCutOff" : 4979.0,                // top limit

		"noiseGenerator" : whiteNoise,                   // whiteNoise or perlinNoise1D or constantNoise
		"functionGenerator" : sineGenerator,         // source data generator

		/* 
		 * noiseToFrequency
		 * Converts the given noise to a midi index on the scale (noteRange), then to a musical note frequency in that range.
		 */
		"noiseToFrequency" : function (noise) {
				return 6.875 * Math.pow(2, (3 + ((Math.abs(noise) % this.noteRange) + 12)) / 12);
		},
		/* 
		 * noiseToMidi
		 * Converts the given noise to a midi index on the scale (noteRange), then to a musical note frequency in that range.
		 */
		"noiseToMidi" : function (noise) {
			//if Math.round
			//return Math.round(noise*127);
			return Math.round(noise*127);
		},
		"midiToFrequency" : function (midi) {
				//document.getElementById("theMIDI").textContent = noise*127-9+"";
				return (440 / 32) * Math.pow(2,(midi-9)/12);
		},

		/*
		 * getNextFrequency
		 * Retrieves the next musical frequency
		 *        1) Get the next source function value
		 *        2) "Noise up" the function value
		 *        3) Convert the noised value to a musical note frequency
		 */
		"getNextFrequency" : function () {
				var frequency = 0;
				var noise = 0;
				var midi = 0;
				var value = this.functionGenerator.getValue();
				
				// get the noise for the function value and convert to frequency.
				var i = 0, maxTries = 20; // guard against a bad function generator with no variation that results in inaudible sound
				while ((frequency < this.frequencyLowCutOff || frequency > this.frequencyHighCutOff) && i < maxTries) {
						noise = this.noiseGenerator.getNoise(value);
						midi = this.noiseToMidi(noise);
						frequency = this.midiToFrequency(midi);
						i++;
				} // while

				// Update output
				document.getElementById("theValue").textContent = frequency+"";
				document.getElementById("theNoise").textContent = noise+"";
				//document.getElementByID("theMIDI").textContent = midi+"";
				document.getElementById("theFreq").textContent = midi+"";
				//alert(frequency);

				return frequency;
		},

		//--------------------------------------------------------------------------------------------
		// public methods
		//--------------------------------------------------------------------------------------------

		/*
		 * setNoiseGenerator
		 * Dynamically sets the noiseGenerator
		 */
		"setNoiseGenerator" : function(noiseOption) {
				switch (noiseOption) {
						case 1:
								this.noiseGenerator = whiteNoise;
								break;
						case 2:
								this.noiseGenerator = perlinNoise1D;
								break;
						default: 
								this.noiseGenerator = constantNoise;
								break;
				} // switch
		},
		
		/*
		 * setFunctionGenerator
		 * Dynamically sets the functionGenerator
		 */
		"setFunctionGenerator" : function(functionOption) {
				// nothing yet
		},
		
		/*
		 * getNextNote
		 * Gets the next note to play after the previous has expired.
		 * Returns a note object with "freq" and "dur" properties.
		 */
		"getNextNote" : function () {
				return { freq : this.getNextFrequency(), dur :  1/2 };
		},

		"getNotesPerBeat" : function () {
				return this.notesPerBeat;
		},

		"getTempo" : function () {
				return this.tempo;
		}
}

// the global instance to the audioNoiseInterface
// 120 bpm, 4 npb
var audioNoise = new audioNoiseInterface(120, 4);

  
        